#ifndef __MESSAGECACHE_H__
#define __MESSAGECACHE_H__

#include <queue>
#include <stack>
#include <string.h>
#include "boost/shared_ptr.hpp"
#include "boost/thread.hpp"

/**
*@file       messagecache.h
*@brief      message cache
*
*@author     graywind
*@version    1.0.0.0
*@data       2012/01/05
*/

///structure of cache unit
struct stMessageCacheUnit
{
public:
        ///pointer type of this structure
        typedef boost::shared_ptr<stMessageCacheUnit>   pointee;
public:
        ///session identifier
        int           SessionId;
        ///used buffer
        unsigned long MsgBytes;
		///message identifier
		unsigned long     MsgId;
        ///message buffer
        char*         Buffer;

public:
        ///@brief    default constructor
	    ///param[in]   bytes_length    buffer length
        stMessageCacheUnit(unsigned int bytes_length):
        SessionId(-1),
        MsgBytes(0),
		MsgId(0),
		Buffer(new char[bytes_length])
        {

        }

		///@brief    copy constructor
		///param[in]   bytes_length    buffer length
		///param[in]  rhs      another unit
		stMessageCacheUnit(unsigned int bytes_length, const stMessageCacheUnit& rhs)
		{
			    Buffer = new char[bytes_length];
			    fill(rhs.SessionId, rhs.MsgId, rhs.MsgBytes, rhs.Buffer);
		}

		///@brief    destructor
		~stMessageCacheUnit()
		{
			discard();
			delete [] Buffer;
			Buffer = 0;
		}

public:
        ///@brief    fill this structure
	    ///param[in]   session_id      session identifier
	    ///param[in]   msg_id          message identifier
	    ///param[in]   msg_bytes       bytes length of the buffer
	    ///param[in]   pMsgBuffer      point to buffer
        ///retval    void
        void fill(int session_id, unsigned long msg_id, unsigned long msg_bytes, void* pMsgBuffer)
        {
                SessionId = session_id;
                MsgBytes  = msg_bytes;
				MsgId     = msg_id;
                memcpy(Buffer, pMsgBuffer, msg_bytes);
        }

		///@brief   fill this structure from another
		///param[in] rhs    another stMessageCacheUnit object
		///retval   void
		void fill(const stMessageCacheUnit& rhs)
        {
			fill(rhs.SessionId, rhs.MsgId, rhs.MsgBytes, rhs.Buffer);
        }

        /***************************************************************************************************************************/
        ///@brief    discard this unit
        ///retval    void
        void discard()
        {
                SessionId = -1;
                MsgBytes  = 0;
				MsgId     = 0;
        }
};

///message cache(FIFO)
class MessageCache
{
private:
		///buffer size
		unsigned int                               m_bufferSize;
        ///list of units
		std::stack<stMessageCacheUnit::pointee>    m_stackUnits;
		///units in used
	    std::queue<stMessageCacheUnit::pointee>    m_queueUse;
		///mutex object
		boost::mutex                               m_mutex;

private:
	    ///@brief   prepare allocate
        ///param[in]  units_num    initialize size
        ///retval   bool
        void pre_allocate(int units_num);

public:
	    ///@brief   defaut constructor
	    ///param[in]   buffer_length    bytes length per buffer
	    ///param[in]   units_num        initialize size    
	    MessageCache(unsigned int buffer_length, int units_num = 0);
		///@brief   destructor
		~MessageCache();

public:
	    ///@brief   store a message
	    ///param[in]   session_id      session identifier
	    ///param[in]   msg_id          message identifier
	    ///param[in]   msg_bytes       bytes length of the buffer
	    ///param[in]   pMsgBuffer      point to buffer
	    ///retval   void
        void write(int session_id, unsigned long msg_id, unsigned long msg_bytes, void* pMsgBuffer);
		///@brief   get the first unit
		///param[inout]   unit     the unit be filled
		///retval   void
        void read(stMessageCacheUnit& unit);
		///@brief   get the valid units count
		///retval   unsigned int
		unsigned int size();
};

/***************************************************************************************************************************/
void
MessageCache::pre_allocate(int units_num)
{
	if(units_num > 0)
	{
		for(int i = 0; i < units_num; ++i)
		{
			stMessageCacheUnit::pointee ptr(new stMessageCacheUnit(m_bufferSize));
			m_stackUnits.push(ptr);
		}
	}
}

/***************************************************************************************************************************/
MessageCache::MessageCache(unsigned int buffer_length, int units_num):
m_bufferSize(buffer_length)
{
	if(buffer_length <= 0)
		std::abort();
	pre_allocate(units_num);
}

/***************************************************************************************************************************/
MessageCache::~MessageCache()
{
	boost::lock_guard<boost::mutex>    lock(m_mutex);
	m_bufferSize = 0;
}

/***************************************************************************************************************************/
void
MessageCache::write(int session_id, unsigned long msg_id, unsigned long msg_bytes, void* pMsgBuffer)
{
	boost::lock_guard<boost::mutex>    lock(m_mutex);
	if(m_stackUnits.size() > 0)
	{
		stMessageCacheUnit::pointee unitPtr = m_stackUnits.top();
		unitPtr->fill(session_id, msg_id, msg_bytes, pMsgBuffer);
		m_queueUse.push(unitPtr);
		m_stackUnits.pop();
	}
	else
	{
		stMessageCacheUnit::pointee unitPtr(new stMessageCacheUnit(m_bufferSize));
		unitPtr->fill(session_id, msg_id, msg_bytes, pMsgBuffer);
		m_queueUse.push(unitPtr);
	}
}

/***************************************************************************************************************************/
void
MessageCache::read(stMessageCacheUnit& unit)
{
    unit.discard();
	boost::lock_guard<boost::mutex>    lock(m_mutex);
	if(m_queueUse.size() > 0)
	{
		stMessageCacheUnit::pointee unitPtr = m_queueUse.front();
		unit.fill(*unitPtr);
		unitPtr->discard();
		m_queueUse.pop();
		m_stackUnits.push(unitPtr);
	}
}


/***************************************************************************************************************************/
unsigned int
MessageCache::size()
{
	boost::lock_guard<boost::mutex>    lock(m_mutex);
	return m_stackUnits.size();
}
#endif
